#include "widget.h"
#include "ui_widget.h"

#include <QFile>
#include <QDomDocument>
#include <QProcess>
#include <QDir>
#include <QDebug>
#include <QTemporaryDir>
#include <QDesktopServices>
#include <QDirIterator>
#include <QUrl>

#include "quazipfile.h"

// 使用 RAII 包装器确保资源清理
class QuaZipRAII {
public:
    QuaZipRAII(QuaZip* zip) : m_zip(zip) {}
    ~QuaZipRAII() {
        if(m_zip && m_zip->isOpen()) {
            m_zip->close();
        }
        delete m_zip;
    }
    operator QuaZip*() { return m_zip; }
    QuaZip* operator->() { return m_zip; }
    QuaZip* get() { return m_zip; }
private:
    QuaZip* m_zip;
    Q_DISABLE_COPY(QuaZipRAII)
};

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);

    generateReport();
}

Widget::~Widget()
{
    delete ui;
}

bool Widget::replaceInDocx(const QString &templatePath,
                          const QString &outputPath,
                          const QMap<QString, QString> &textReplacements,
                          const QMap<QString, QString> &imageReplacements)
{
    QTemporaryDir tempDir;
    if (!tempDir.isValid()) {
        qWarning() << "无法创建临时目录";
        return false;
    }


    // 1 解压 docx
    QString unzipPath = tempDir.path();
    if (!extractFile(templatePath, unzipPath)) {
        qWarning() << "解压失败!";
        return false;
    }

    qWarning() << unzipPath;
    // 2 替换文本内容
    QString docXmlPath = unzipPath + "/word/document.xml";
    QFile file(docXmlPath);
    if (!file.open(QIODevice::ReadOnly)) {
        qWarning() << "无法打开document.xml";
        return false;
    }

    QDomDocument doc;
    if (!doc.setContent(&file)) {
        file.close();
        qWarning() << "解析XML失败";
        return false;
    }
    file.close();

    // 文本替换
    QString xml = doc.toString();
    for (auto it = textReplacements.begin(); it != textReplacements.end(); ++it) {
        QString key = QString("${%1}").arg(it.key());
        QString value = it.value();
        xml.replace(key, value);
    }
    xml.replace("${FileHead}", "我是表头");

    if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
        qWarning() << "无法写入document.xml";
        return false;
    }
    file.write(xml.toUtf8());
    file.close();

    // 3 替换图片
    if (!imageReplacements.isEmpty()) {
        QString mediaPath = unzipPath + "/word/media/";
        QDir mediaDir(mediaPath);

        // 确保media目录存在
        if (!mediaDir.exists()) {
            mediaDir.mkpath(".");
        }

        // 处理图片替换
        for (auto it = imageReplacements.constBegin(); it != imageReplacements.constEnd(); ++it) {
            QString placeholder = it.key();       // 占位符名称，如 "logo"
            QString imagePath = it.value();       // 新图片路径
            // 目标图片路径
            QString destPath = mediaPath + placeholder;

            // 复制图片到media目录
            if (!copyWithOverwrite(imagePath, destPath)) {
                qWarning() << "图片复制失败:" << imagePath << "->" << destPath;
                continue;
            }
        }
    }

    // 准备表格数据
    QList<TableValue> tableValueList = {
        { "文档1", "描述1", "说明1", "种类1" },
        { "文档2", "描述2", "说明1", "种类2" },
        { "文档3", "描述3", "说明1", "种类3" },
        { "文档4", "描述4", "说明1", "种类4" },
        { "文档5", "描述5", "说明1", "种类5" },
        { "文档6", "描述6", "说明1", "种类6" },
        { "末尾1", "末尾2", "末尾3", "末尾4" },
    };
    // 4 动态新增替换表格
    if (!replayTableIndex(docXmlPath, tableValueList)) {
        qWarning() << "表格数据替换失败！";
        return false;
    }

    // 5 压缩回 docx
    if (!compressFile(outputPath, unzipPath)) {
        qWarning() << "压缩失败!";
        return false;
    }

    return true;
}

bool Widget::convertDocxToPdf(const QString &docxPath, const QString &pdfOutputDir)
{
    QProcess process;
    QString libreOfficeCmd = "";

    // 根据自己的安装路径设置，一定要绝对路径，相对路径不行，会找不到soffice
#ifdef Q_OS_UNIX
    libreOfficeCmd = "/opt/libreoffice25.8/program/soffice"; // Linux路径
#else
    libreOfficeCmd = "D:/libreoffice/program/soffice.exe" // Windows路径
#endif

    QStringList args = {
        "--headless",
        "--convert-to", "pdf",
        "--outdir", pdfOutputDir,
        docxPath
    };

    process.start(libreOfficeCmd, args);
    if (!process.waitForFinished(15000)) {
        qWarning() << "PDF转换超时:" << process.errorString();
        return false;
    }
    return process.exitCode() == 0;
}

void Widget::generateReport()
{
    // 文本替换数据
    QMap<QString, QString> textData {
        {"Name", "Jtom"},
        {"Age", "111"},
        {"Description", "这是一段描述信息ABCdef123"},
        {"Table1", "固定表格的数据1"},
        {"Table2", "其他其他"},
        {"Table3", "啦啦啦"},
        {"Special1", "我很特别"},
        {"Special2", "俺也一样"},
        {"Special3", "我知道"}
    };

    // 图片替换数据 (Word中的图片名字 -> 图片路径)
    QMap<QString, QString> imageData {
        {"image1.png", "Image/1.png"},
        {"image2.png", "Image/2.png"},
        {"image3.png", "Image/3.png"},
        {"image4.png", "Image/图片2.png"},
        {"image5.png", "Image/图片3.png"}
    };

    QString currentPath = QCoreApplication::applicationDirPath();
    QString templatePath = currentPath + "/input.docx";
    QString outputDocx = currentPath + "/report.docx";
    QString outputPdf = currentPath + "/report.pdf";

    if (replaceInDocx(templatePath, outputDocx, textData, imageData)) {
        qDebug() << "DOCX 创建成功: " << outputDocx;

        if (convertDocxToPdf(outputDocx, QFileInfo(outputPdf).absolutePath())) {
            qDebug() << "PDF 创建成功: " << outputPdf;
            // 打开生成的PDF
            QDesktopServices::openUrl(QUrl::fromLocalFile(outputPdf));
        } else {
            qWarning() << "PDF 生成失败";
        }
    } else {
        qWarning() << "报告生成失败";
    }
}



bool Widget::copyWithOverwrite(const QString &source, const QString &destination)
{
    if(source == destination) return true;  // 相同路径直接返回

    QFileInfo srcInfo(source);
    if(!srcInfo.exists()) return false;  // 源文件不存在检查

    QFile dstFile(destination);
    if(dstFile.exists()) {
        if(!dstFile.remove()) return false;  // 删除已存在目标文件
    }

    QDir dstDir(QFileInfo(destination).path());
    if(!dstDir.exists()) {
        if(!dstDir.mkpath(".")) return false;  // 创建目标目录
    }

    return QFile::copy(source, destination);  // 执行拷贝
}

bool Widget::replayTableIndex(const QString &templatePath, QList<Widget::TableValue> TableData)
{
    QString docXmlPath = templatePath;
    QFile file(docXmlPath);
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) return false;


    QDomDocument doc;
    if (!doc.setContent(&file)) {
        file.close();
        return false;
    }
    file.close();

    // 查找所有表格
    QDomNodeList tables = doc.elementsByTagName("w:tbl");
    if (tables.isEmpty()) return false;


    QDomElement table = { };
    for (int j = 0; j < tables.count(); j++) {
        table = tables.at(j).toElement();   // 获取表格
        QString str = table.text();         // 获取表格内的所有文本
        // 通过判断字符串内是否包含占位字符串，从而得知当前表格是否是需要替换文本的表格
        if (str.contains("${Value1}")) {    // 可以判断是占位字符串里面任意，因为他们理论上都是唯一的
            break;
        } else {
            table = { };
        }
    }

    // 判断是否找到了匹配的表格
    if (table.isNull()) {
        qWarning() << "没有找到匹配的表格！";
        return false;
    }

    // 查找表格内的所有行
    QDomNodeList rows = table.elementsByTagName("w:tr");
    if (rows.size() < 2) return false;


    QDomNode templateRow = { }; // 占位模板行
    for (int j = 0; j < rows.count(); ++j) {
        templateRow = rows.at(j);           // 获得占位模板行
        QString str = templateRow.toElement().text();         // 获取一行表格内的所有文本
        // 通过判断字符串内是否包含占位字符串，从而得知当前行是否是占位模板行
        if (str.contains("${Value1}")) {    // 可以判断是占位字符串里面任意，因为他们理论上都是唯一的
            break;
        } else {
            templateRow = { };
        }
    }

    // 判断是否找到了 占位模板行
    if (table.isNull()) {
        qWarning() << "没有找到 占位模板行！";
        return false;
    }


    // 表格动态插入行和文本替换
    for (int i = 0; i < TableData.size(); ++i) {
        // 克隆一个新的占位模板行，相当于插入了一行占位模板行
        QDomNode newRow = templateRow.cloneNode(true);
        // 替换文本
        //QString xmlString = newRow.toElement().ownerDocument().toString();

        // 临时存储站位模板行，用于动态插入
        newRow = newRow.toElement();
        // 查找一行表格内的所有单元格
        QDomNodeList cells = newRow.toElement().elementsByTagName("w:t");
        for (int j = 0; j < cells.count(); ++j) {
            QDomElement cell = cells.at(j).toElement();
            QString text = cell.text(); // 获得单元格内的文本


            if (text.contains("${Value1}")) {
                text = TableData[i].document;

            } else if (text.contains("${Value2}")) {
                text = TableData[i].description;

            } else if (text.contains("${Value3}")) {
                text = TableData[i].explain;

            } else if (text.contains("${Value4}")) {
                text = TableData[i].kind;
            }

            // 重新设置单元格里面的文本，相当于替换
            cell.firstChild().setNodeValue(text);
        }

        // 在末尾插入新的一行 占位模板行
//        table.appendChild(newRow);
        table.insertAfter(newRow, table.lastChild());
    }

    table.removeChild(templateRow); // 删除原始模板行

    // 保存 document.xml
    if (!file.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) return false;
    QTextStream out(&file);
    doc.save(out, 2);
    file.close();

    return true;
}


bool Widget::compressFile(const QString &zipPath, const QString &sourceDir)
{
    // 使用 RAII 管理资源
    QuaZipRAII newZip(new QuaZip(zipPath));

    if (!newZip->open(QuaZip::mdCreate)) {
        qWarning() << "Failed to create ZIP file:" << zipPath
                   << "Error:" << newZip->getZipError();
        return false;
    }

    QDir sourceDirObj(sourceDir);

    QSet<QString> addedDirs;
    const int blockSize = 65536;  // 64KB 块大小

    // 递归处理目录
    QDirIterator it(sourceDir, QDir::AllEntries | QDir::Hidden | QDir::System | QDir::NoDotAndDotDot,
                    QDirIterator::Subdirectories);

    while (it.hasNext()) {
        QString filePath = it.next();
        QFileInfo fi(filePath);
        QString relativePath = sourceDirObj.relativeFilePath(filePath);

        // 处理目录
        if (fi.isDir()) {
            QString zipDirPath = relativePath + '/';
            if (!addedDirs.contains(zipDirPath)) {
                QuaZipFile zipFile(newZip);
                QuaZipNewInfo newInfo(zipDirPath);
//                newInfo.externalAttr = (fi.permissions() | 0x10) << 16; // 保留权限+目录标志

                if (!zipFile.open(QIODevice::WriteOnly, newInfo)) {
                    qWarning() << "Failed to create directory entry:" << zipDirPath
                               << "Error:" << zipFile.getZipError();
                    continue;
                }
                zipFile.close();
                addedDirs.insert(zipDirPath);
            }
            continue;
        }

        // 处理文件
        QFile sourceFile(filePath);
        if (!sourceFile.open(QIODevice::ReadOnly)) {
            qWarning() << "Failed to open source file:" << filePath;
            continue;
        }

        QuaZipFile zipFile(newZip);
        QuaZipNewInfo newInfo(relativePath, filePath);
//        newInfo.externalAttr = fi.permissions() << 16; // 保留文件权限

        if (!zipFile.open(QIODevice::WriteOnly, newInfo)) {
            qWarning() << "Failed to open ZIP entry:" << relativePath
                       << "Error:" << zipFile.getZipError();
            sourceFile.close();
            continue;
        }

        // 分块读写提高大文件处理效率
        qint64 totalBytes = 0;
        char buffer[blockSize];
        while (!sourceFile.atEnd()) {
            qint64 bytesRead = sourceFile.read(buffer, blockSize);
            if (bytesRead <= 0) break;

            qint64 bytesWritten = zipFile.write(buffer, bytesRead);
            if (bytesWritten != bytesRead) {
                qWarning() << "Write error for:" << relativePath
                           << "Expected:" << bytesRead << "Actual:" << bytesWritten;
                break;
            }
            totalBytes += bytesWritten;
        }

        zipFile.close();
        sourceFile.close();

        if (totalBytes != fi.size()) {
            qWarning() << "File size mismatch:" << relativePath
                       << "Original:" << fi.size() << "Compressed:" << totalBytes;
        }
    }

    return newZip->getZipError() == UNZ_OK;
}

bool Widget::extractFile(const QString &zipPath, const QString &targetDir)
{
    QuaZipRAII zip(new QuaZip(zipPath));
    if (!zip->open(QuaZip::mdUnzip)) {
        qWarning() << "Failed to open ZIP file:" << zipPath
                   << "Error:" << zip->getZipError();
        return false;
    }

    QDir targetDirObj(targetDir);
    if (!targetDirObj.exists() && !targetDirObj.mkpath(".")) {
        qWarning() << "Failed to create target directory:" << targetDir;
        return false;
    }

    const int blockSize = 65536;  // 64KB 块大小
    QuaZipFileInfo fileInfo;
    QuaZipFile file(zip);

    for (bool more = zip->goToFirstFile(); more; more = zip->goToNextFile()) {
        if (!zip->getCurrentFileInfo(&fileInfo)) {
            qWarning() << "Failed to get file info, skipping. Error:" << zip->getZipError();
            continue;
        }

        QString absPath = targetDirObj.absoluteFilePath(fileInfo.name);
        QFileInfo fi(absPath);

        // 创建目录结构
        if (!QDir().mkpath(fi.path())) {
            qWarning() << "Failed to create path:" << fi.path();
            continue;
        }

        // 处理目录
        if (fileInfo.name.endsWith('/')) {
            QDir dir(absPath);
            if (!dir.exists() && !dir.mkpath(".")) {
                qWarning() << "Failed to create directory:" << absPath;
            }
            continue;
        }

        // 打开文件
        if (!file.open(QIODevice::ReadOnly)) {
            qWarning() << "Failed to open ZIP entry:" << fileInfo.name
                       << "Error:" << file.getZipError();
            continue;
        }

        // 创建目标文件
        QFile outFile(absPath);
        if (!outFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
            qWarning() << "Failed to open output file:" << absPath;
            file.close();
            continue;
        }

        // 分块读写
        char buffer[blockSize];
        qint64 totalBytes = 0;
        while (!file.atEnd()) {
            qint64 bytesRead = file.read(buffer, blockSize);
            if (bytesRead <= 0) break;

            qint64 bytesWritten = outFile.write(buffer, bytesRead);
            if (bytesWritten != bytesRead) {
                qWarning() << "Write error for:" << absPath
                           << "Expected:" << bytesRead << "Actual:" << bytesWritten;
                break;
            }
            totalBytes += bytesWritten;
        }

        outFile.close();
        file.close();

        // 设置文件权限
//        if (fileInfo.externalAttr != 0) {
//            QFile::setPermissions(absPath,
//                static_cast<QFile::Permissions>(fileInfo.externalAttr >> 16));
//        }

        // 设置文件时间戳
        QDateTime dt;
        dt.setDate(QDate(fileInfo.dateTime.date().year() + 1900,
                         fileInfo.dateTime.date().month() + 1,
                         fileInfo.dateTime.date().day()));
        dt.setTime(QTime(fileInfo.dateTime.time().hour(),
                         fileInfo.dateTime.time().minute(),
                         fileInfo.dateTime.time().second()));
        QFile(absPath).setFileTime(dt, QFileDevice::FileModificationTime);

        if (totalBytes != fileInfo.uncompressedSize) {
            qWarning() << "Size mismatch for:" << absPath
                       << "Expected:" << fileInfo.uncompressedSize
                       << "Actual:" << totalBytes;
        }
    }

    return zip->getZipError() == UNZ_OK;
}
